#ifndef DEF_LLIST
#define DEF_LLIST
//#include <pthread.h>

/* nodes type */
typedef struct LListNod {
	void* data;		// generic data pointer
	struct LListNod * next;	// next node
		
} LListNod;

/* list of LListNod */
typedef struct {
	struct LListNod * first;		// linked list head
	struct LListNod * last;			// linked list tail
	struct LListNod * current;		// current node - used for "look into list"
	int size;
	pthread_mutex_t mutex;			// linked list mutex
	pthread_mutex_t mutex_non_empty;	// mutex of condition var
	pthread_cond_t cond_non_empty;		// signal non empty list 
} LList;


/* list initialisation */
int llist_init(LList * list)
{
	if(list == NULL){
		msg("llist-init","list doesn't exists");
		return EXIT_FAILURE;
	}
	
	list->first = NULL;
	list->last = NULL;
	list->current = NULL;
	list->size = 0;

	pthread_mutex_init(& (list->mutex), NULL);
}

/* get list size */
int llist_size(LList list)
{
    return list.size;
}

/* get current data elt */
int llist_get_current(LList * list, void ** currentElt)
{
	if(list == NULL) {
	  msg("llist-get-current","list doesn't exists");
	  return EXIT_FAILURE;
	}
	
	if (list->current == NULL) {
		*currentElt = NULL;
		msg("llist-get-current","list is empty");
		return EXIT_FAILURE;
	}
	
	*currentElt = (void *) list->current->data;
	
	// point to the next node
	list->current = list->current->next;
	
	msg("llist-get-current","Terminated");
	
	return EXIT_SUCCESS;
}

/* get the first data elt of the list */
int llist_get_first(LList list, void ** firstElt)
{
	
	if (list.first == NULL) {
		*firstElt = NULL;
		msg("llist-get-first","list is empty");
		return EXIT_FAILURE;
	}
	
	*firstElt = (void *) (list.first)->data;
	
	msg("llist-get-first","Terminated");
	return EXIT_SUCCESS;	
}

/* get the last data elt of the list */
int llist_get_last(LList list, void ** lastElt)
{
	
	if (list.last == NULL) {
		*lastElt = NULL;
		msg("llist-get-last","list is empty");
		return EXIT_FAILURE;
	}
	
	*lastElt = (void *) (list.last)->data;
	msg("llist-get-last","Terminated.");
	return EXIT_SUCCESS;	
}

/* insert at last of the list */
int llist_insert_last(LList * list, void * newElt)
{
	if(list == NULL){
	 msg("llist-insert-last","List doesnt exist");
	 return EXIT_FAILURE;
	}
	LListNod * newNod = malloc( sizeof(LListNod));
	if (newNod == NULL) {
		msg("llist-insert-last","malloc error");
		return EXIT_FAILURE;
	}
	// fill the new node data
	newNod->data = newElt;
	
	
	/* case : empty list */
	if ( list->size == 0 )
	{
		list->first = newNod;
		list->last = newNod;
		list->current = newNod;
		newNod->next = list->first;
		
	} else 
	{
		(list->last)->next = newNod;
		newNod->next = list->first;
		list->last = newNod;
	}
	
	msg("llist-insert-last","Terminated");
	(list->size)++;
	return EXIT_SUCCESS;
}

/* insert at first of the list */
int llist_insert_first(LList * list, void * newElt)
{
	if(list == NULL){
	 msg("llist-insert-first","List doesnt exists");
	 return EXIT_FAILURE;
	}
	
	LListNod * newNod = malloc( sizeof(LListNod));
	if (newNod == NULL){
		msg("llist-insert-first","malloc error"); 
		return EXIT_FAILURE;
	}
	
	// fill the new node data
	newNod->data = newElt;
	
	
	/* case : empty list */
	if ( list->size == 0 )
	{
		list->first = newNod;
		list->last = newNod;
		list->current = newNod;
		newNod->next = newNod;
		
	} else 
	{
		newNod->next = list->first;
		list->first = newNod;
		list->last->next = newNod;
		
	}
	
	msg("llist-insert-first","Terminated");
	(list->size)++;
	return EXIT_SUCCESS;
}

/* open list for "parcours" */
int llist_open_list(LList * list)
{
	if(list == NULL){
	 msg("llist-open-list","List doesnt exist");
	 return EXIT_FAILURE;
	}
	
	list->current = list->first;
	msg("llist-open-list","Terminated");
	return EXIT_SUCCESS;	
}

/* is the current item the last of the list */
int llist_end_reached(LList list)
{
	msg("llist-end-reached","Terminated");
	return list.current == list.last;
}


/* search for data elt */
int llist_search_elt(LList list, void * eltToSearch, size_t size)
{
	LListNod * current = list.first;
	
	if(list.size == 0){
	 msg("llist-search-elt","Empty list");
	 return 0;
	}
	
	while ((memcmp(current->data, eltToSearch, size) != 0) && ((current = current->next) != list.last) );
	
	if ( (current == list.last) && (memcmp(current->data, eltToSearch, size) != 0) ){
		msg("llist-search-elt","elt non trouve");
		return 0;
	}
	msg("llist-search-elt","elt trouve");
	return 1;	
}

/* delete data elt from list */
int llist_delete_elt(LList * list, void * eltToDelete)
{
	if(list == NULL) return EXIT_FAILURE;
	
	LListNod * current = list->first;
	LListNod * previous = list->first;
	
	if(list->size == 0){
	 msg("llist-delete-elt","empty list");
	 return EXIT_FAILURE;
	}
	
	while ((current->data != eltToDelete) && (current != list->last) ){
		previous = current;
		current = current->next;
	}
	
	if ( (current == list->last) && (current->data != (void *)eltToDelete) ){
		msg("llist-delete-elt","Elt doesnt exist");
		return EXIT_FAILURE;
	}
	
	if(list->current == current) list->current = current->next;
	
	if(current == list->first) list->first = current->next;
	
	if(current == list->last) list->last = previous;
	
	previous->next = current->next;

	free(current);
	
	if(list->size == 1) {
		list->first = NULL;
		list->last = NULL;
		list->current = NULL;
	}
	
	(list->size)--;	
	msg("llist-delete-elt","Terminated");
	return EXIT_SUCCESS;
}

/* clear the list (destroy) */
int llist_clear(LList * list)
{
	if(list == NULL) return EXIT_FAILURE;
	
	LListNod * tmp = list->first;
	LListNod * del = list->first;	
	
	list->first = NULL;
	list->last = NULL;
	list->current =NULL;
	
	while(list->size != 0)
	{
		tmp = del->next;
		free(del);
		del = tmp;
		(list->size)--;
	}
	
	msg("llist-clear","Terminated");
	return EXIT_SUCCESS;
}

/* print data elts */
int llist_print(LList list)
{
	LListNod * tmp;
	int i = 1;
	
	printf(":::: Printing (%d) Elts of list (%p-%p) :::::\n", list.size, list.first, list.last);
	
	if(list.size == 0) return EXIT_SUCCESS;
	
	tmp = list.first;
	while( tmp != list.last)
	{
		printf(":: Elt %p at %p \n", tmp->data, tmp);
		tmp = tmp->next;
	}
	
	printf(":: Elt %p at %p \n", tmp->data, tmp);
	
	msg("llist-print","Terminated");
	return EXIT_SUCCESS;
}

int msg(char * title, char * mesg)
{
	//printf("%s:::%s\n", title, mesg);
	return 0;
}

#endif

